#!/usr/bin/env python
# -*- coding: utf-8 -*-
#coding=utf8

import os
import sys
import struct

# 数据类型定义，同时也是作为该数据的长度
buftype_unknow		= 0
buftype_bool		= 1
buftype_uint8		= 1
buftype_uint16		= 2
buftype_uint32		= 4
buftype_uint64		= 8
buftype_int8		= 1
buftype_int16		= 2
buftype_int32		= 4
buftype_int64		= 8
buftype_float		= 4
buftype_double		= 8
buftype_string_s	= 254	# 特殊类型，它的数据段由 １位数据长度 + 数据　构成
buftype_string		= 255	# 特殊类型，它的数据段由 两位数据长度 + 数据 构成

class CsbufParser:
    ''' csbuf 协议解析器 '''
    def __init__(self):
        self._data = bytearray("", "utf-8")
        self._cursor = 0
        self._popStack = []

    def setData(self, data):
        self._data = data
        self._cursor = 0

    def getData(self):
        return self._data

    def clear(self):
        self._data = bytearray("", "utf-8")
        self._cursor = 0
        self._popStack = []

    def setBool(self, d, pos):
        if (not d and 0 != pos):
            return
        self._data = self._data + struct.pack("3B", pos, buftype_bool, d and 1 or 0)

    def setUint8(self, d, pos):
        if 0 == d and 0 != pos:
            return
        self._data = self._data + struct.pack("3B", pos, buftype_uint8, d)

    def setUint16(self, d, pos):
        if 0 == d and 0 != pos:
            return
        self._data = self._data + struct.pack(">2BH", pos, buftype_uint16, d)

    def setUint32(self, d, pos):
        if 0 == d and 0 != pos:
            return
        self._data = self._data + struct.pack(">2BI", pos, buftype_uint32, d)

    def setUint64(self, d, pos):
        if 0 == d and 0 != pos:
            return
        self._data = self._data + struct.pack(">2BQ", pos, buftype_uint64, d)

    def setInt8(self, d, pos):
        if 0 == d and 0 != pos:
            return
        self._data = self._data + struct.pack("2Bb", pos, buftype_int8, d)

    def setInt16(self, d, pos):
        if 0 == d and 0 != pos:
            return
        self._data = self._data + struct.pack(">2Bh", pos, buftype_int16, d)

    def setInt32(self, d, pos):
        if 0 == d and 0 != pos:
            return
        self._data = self._data + struct.pack(">2Bi", pos, buftype_int32, d)

    def setInt64(self, d, pos):
        if 0 == d and 0 != pos:
            return
        self._data = self._data + struct.pack(">2Bq", pos, buftype_int64, d)

    def setFloat(self, d, pos):
        if d > -0.00000001 and d < 0.00000001 and 0 != pos:
            return
        self._data = self._data + struct.pack(">2Bf", pos, buftype_float, d)

    def setDouble(self, d, pos):
        if d > -0.00000001 and d < 0.00000001 and 0 != pos:
            return
        self._data = self._data + struct.pack(">2Bd", pos, buftype_double, d)

    # 只支持2^16大小的字符串
    def setString(self, d, pos):
        d = d.encode("utf-8")
        n = len(d)
        if n == 0 and 0 != pos:
            return
        if n > 255:
            self._data = self._data + struct.pack(">2BH", pos, buftype_string, n)
        else:
            self._data = self._data + struct.pack(">3B", pos, buftype_string_s, n)
        if n > 0:
            self._data = self._data + d

    # 数组，存储数组元素个数，数据类型由之后的设置具体值决定。为了能够正确的跳过数组区域，我们约定数组域内的所有值的 pos 都设置成 0
    def setArray(self, d, pos):
        self.setUint8(d, pos)

    # 消息结构体，它的长度无法预先确定，但我们需要知道他的长度来正确跳过及正确读取，所以这个函数必须与 endSetMessage 成对出现。
    def setMessage(self, pos):
        self._data = self._data + struct.pack(">2BH", pos, buftype_string, 0)
        self._popStack.append(len(self._data))

    def endSetMessage(self):
        if len(self._popStack) > 0:
            pos = self._popStack.pop()
            n = len(self._data) - pos
            if n > 0:
                s = struct.pack(">H", n)
                self._data[pos - 2] = s[0]
                self._data[pos - 1] = s[1]
            elif 0 != self._data[pos - 4]:
                self._data = self._data[0 : pso-4]


    # 获取数据
    def getBool(self, pos):
        ret = self._getValue(1, pos, "B", 0)
        if 0 == ret:
            return False
        return True

    def getUint8(self, pos):
        return self._getValue(1, pos, "B", 0)

    def getUint16(self, pos):
        return self._getValue(2, pos, ">H", 0)

    def getUint32(self, pos):
        return self._getValue(4, pos, ">I", 0)

    def getUint64(self, pos):
        return self._getValue(8, pos, ">Q", 0)

    def getInt8(self, pos):
        return self._getValue(1, pos, "b", 0)

    def getInt16(self, pos):
        return self._getValue(2, pos, ">h", 0)

    def getInt32(self, pos):
        return self._getValue(4, pos, ">i", 0)

    def getInt64(self, pos):
        return self._getValue(8, pos, ">q", 0)

    def getFloat(self, pos):
        return self._getValue(4, pos, ">f", 0)

    def getDouble(self, pos):
        return self._getValue(8, pos, ">d", 0)

    def getString(self, pos):
        ret = ""
        if self._setCursorWithPos(pos):
            s = self._getSize()
            t = self._getByte(self._cursor+1)
            if self._cursor + 3 < s and buftype_string == t:
                n = self._unpack(">H", self._data[self._cursor + 2 : self._cursor + 4])
                if n > 0 and self._cursor + 4 + n <= s:
                    ret = self._data[self._cursor + 4: self._cursor + 4 + n]
                self._cursor = self._cursor + 4 + n
            elif self._cursor + 2 < s and buftype_string_s == t:
                n = self._getByte(self._cursor + 2)
                if n > 0 and self._cursor + 3 + n <= s:
                    ret = self._data[self._cursor + 3: self._cursor + 3 + n]
                self._cursor = self._cursor + 3 + n
        if "" != ret:
            ret = ret.decode("utf-8")
        return ret

    # 获取数组的元素个数
    def getArray(self, pos):
        return self.getUint8(pos)

    # 获取消息的数据块大小，当调用这个函数之后就会进入获取子消息结构方式，只有调用相应的 endGetMessage 才会退出
    def getMessage(self, pos):
        ret = 0
        if self._setCursorWithPos(pos):
            s = self._getSize()
            if self._cursor + 3 < s and buftype_string == self._getByte(self._cursor+1):
                n = self._unpack(">H", self._data[self._cursor+2 : self._cursor+4])
                self._cursor = self._cursor + 4
                self._popStack.append(n + self._cursor)
            else:
                self._popStack(self._cursor)
        else:
            self._popStack.append(self._cursor)
        return ret

    def endGetMessage(self):
        if len(self._popStack) > 0:
            self._cursor = self._popStack.pop()

    def _getValue(self, n, pos, fmt, default):
        if not self._setCursorWithPos(pos):
            return default
        s = self._getSize()
        if self._cursor + 1 + n < s and n == self._getByte(self._cursor + 1):
            ret = self._unpack(fmt, self._data[self._cursor + 2 : self._cursor + 2 + n])
            self._cursor = self._cursor + 2 + n
            return ret
        return default

    def _setCursorWithPos(self, pos):
        s = self._getSize()
        if self._cursor + 1 < s:
            p = self._getByte(self._cursor)
            if p == pos:
                return True
            elif p < pos:
                t = self._getByte(self._cursor + 1)
                if buftype_string == t:
                    self._cursor = self._cursor + 2
                    if self._cursor + 2 >= s:
                        return False
                    self._cursor = self._cursor + 2 + self._unpack(">H", self._data[self._cursor : self._cursor + 2])
                elif buftype_string_s == t:
                    self._cursor = self._cursor + 2
                    if self._cursor + 2 >= s:
                        return False
                    self._cursor = self._cursor + 1 + self._getByte(self._cursor)
                else:
                    self._cursor = self._cursor + 2 + t
                return self._setCursorWithPos(pos)
        return False
    
    def _unpack(self, fmt, buf):
        return struct.unpack(fmt, buf)[0]
    
    def _getByte(self, pos):
        return self._data[pos]

    def _getSize(self):
        if len(self._popStack) > 0:
            return self._popStack[len(self._popStack) - 1]
        return len(self._data)
    
    
if __name__ == "__main__":
    p1 = CsbufParser()
    p2 = CsbufParser()
    
    p1.setBool(True, 1)
    p1.setUint16(20, 2)
    p1.setString(u"测试", 3)
    p1.setString(u"测试", 4)
    p1.setMessage(5)
    p1.setInt16(-2000, 1)
    p1.setString("test,", 2)
    p1.endSetMessage()
    p1.setString("tttttttttt tttttttttt tttttttttt tttttttttt tttttttttt tttttttttt tttttttttt tttttttttt tttttttttt tttttttttt tttttttttt tttttttttt tttttttttt tttttttttt tttttttttt tttttttttt tttttttttt tttttttttt tttttttttt tttttttttt tttttttttt tttttttttt tttttttttt tttttttttt tttttttttt 111", 6)
    p1.setArray(2, 7)
    p1.setString("efg", 0)
    p1.setString("hijk", 0)
    p1.setMessage(8)
    p1.setInt32(-2000, 1)
    p1.setString("test,", 2)
    p1.endSetMessage()
    
    p2.setData(p1.getData())
    
    print(p2.getBool(1), p2.getUint16(2), p2.getString(3), p2.getString(4))
    p2.getMessage(5)
    print(p2.getInt16(1), p2.getString(2))
    p2.endGetMessage()
    print(p2.getString(6), "\n", p2.getArray(7), p2.getString(0), p2.getString(0))
    p2.getMessage(8)
    print(p2.getInt32(1), p2.getString(2))
    p2.endGetMessage()
    
